package index;

import Common.*;
import heap.*;
import exceptions.*;
import java.io.*;

import storage.data.Page;
import storage.data.PageID;
import Transaction.*;
public class LeafPage extends BTPage {

	public LeafPage(PageID pageno, int keyType,Transaction tx,String table) throws Exception {
		super(pageno, keyType);
		setType(NodeType.LEAF,tx,table,pageno);
	}

	public LeafPage(Page page, int keyType,Transaction tx,String table) throws Exception {
		super(page, keyType);
		setType(NodeType.LEAF,tx,table,curPage);
	}

	public LeafPage(int keyType,Transaction tx,String table) throws Exception {
		super(keyType,tx,table);
		setType(NodeType.LEAF,tx,table,curPage);
	}

	/**
	 * Inserts a key, rid value into the leaf node.
	 * 
	 * @param key
	 * @param dataRid
	 * @return
	 * @throws Exception
	 */
	public RID insertRecord(KeyClass key, RID dataRid,Transaction tx,String table) throws Exception {
		KeyDataEntry entry;

		entry = new KeyDataEntry(key, dataRid);
	
		return insertRecord(entry,tx,table);

	}

	/**
	 * Iterators. One of the two functions: getFirst and getNext which provide
	 * an iterator interface to the records on a BTLeafPage.
	 */
	public KeyDataEntry getFirst(RID rid,Transaction tx,String table) throws Exception {

		KeyDataEntry entry;

		rid.pageNo = getCurPage(tx,table);
		rid.slotNo = 0; // begin with first slot
		
		//System.out.println("rid:"+rid.pageNo.pageId);
		if (getSlotCnt(tx,table) <= 0) {
			
			return null;
		}

		entry = BT.getEntryFromBytes(getData(), getSlotOffset(0,tx,table),
				getSlotLength(0,tx,table), keyType, NodeType.LEAF,tx,table);

		return entry;

	}

	public KeyDataEntry getNext(RID rid,Transaction tx,String table) throws Exception {
		KeyDataEntry entry;
		int i;

		rid.slotNo++; // must before any return;
		i = rid.slotNo;

		if (rid.slotNo >= getSlotCnt(tx,table)) {
			return null;
		}

		entry = BT.getEntryFromBytes(getData(), getSlotOffset(i,tx,table),
				getSlotLength(i,tx,table), keyType, NodeType.LEAF,tx,table);

		return entry;

	}

	/**
	 * getCurrent returns the current record in the iteration; it is like
	 * getNext except it does not advance the iterator.
	 */

	public KeyDataEntry getCurrent(RID rid,Transaction tx,String table) throws Exception {
		rid.slotNo--;
		return getNext(rid,tx,table);
	}

	/**
	 * delete a data entry in the leaf page.
	 * 
	 * @param dEntry
	 * @return
	 * @throws Exception
	 */
	public boolean delEntry(KeyDataEntry dEntry,Transaction tx,String table) throws Exception {
		KeyDataEntry entry;
		RID rid = new RID();
		//System.out.println(dEntry.key+" "+dEntry.data);
		for (entry = getFirst(rid,tx,table); entry != null; entry = getNext(rid,tx,table)) {
			//System.out.println("entry:"+" "+entry.key+" "+entry.data);
			if (entry.equals(dEntry)) {
				/*
				 * if (super.deleteSortedRecord(rid) == false) throw new
				 * LeafDeleteException(null, "Delete record failed");
				 */
				super.deleteSortedRecord(rid,tx,table);
				
				return true;
			}

		}
		return false;

	}

	/**
	 * used in full delete
	 * 
	 * @param leafPage
	 *            the sibling page of this. Input parameter.
	 * @param parentIndexPage
	 *            the parant of leafPage and this. Input parameter.
	 * @param direction
	 *            -1 if "this" is left sibling of leafPage ; 1 if "this" is
	 *            right sibling of leafPage. Input parameter.
	 * @param deletedKey
	 *            the key which was already deleted, and cause redistribution.
	 *            Input parameter.
	 * @exception LeafRedistributeException
	 * @return true if redistrbution success. false if we can not redistribute
	 *         them.
	 */
	boolean redistribute(LeafPage leafPage, IndexPage parentIndexPage,
			int direction, KeyClass deletedKey,Transaction tx,String table) throws Exception {
		boolean st;
		// assertion: leafPage pinned
		try {
			if (direction == -1) { // 'this' is the left sibling of leafPage
				if ((getSlotLength(getSlotCnt(tx,table) - 1,tx,table) + availableSpace(tx,table) + 8 /* 2*sizeof(slot) */) > ((MAX_FRAME - DPFIXED) / 2)) {
					// cannot spare a record for its underflow sibling
					return false;
				} else {
					// move the last record to its sibling

					// get the last record
					KeyDataEntry lastEntry;
					lastEntry = BT.getEntryFromBytes(getData(),
							getSlotOffset(getSlotCnt(tx,table) - 1,tx,table),
							getSlotLength(getSlotCnt(tx,table) - 1,tx,table), keyType,
							NodeType.LEAF,tx,table);

					// get its sibling's first record's key for adjusting parent
					// pointer
					RID dummyRid = new RID();
					KeyDataEntry firstEntry;
					firstEntry = leafPage.getFirst(dummyRid,tx,table);

					// insert it into its sibling
					leafPage.insertRecord(lastEntry,tx,table);

					// delete the last record from the old page
					RID delRid = new RID();
					delRid.pageNo = getCurPage(tx,table);
					delRid.slotNo = getSlotCnt(tx,table) - 1;
					if (deleteSortedRecord(delRid,tx,table) == false)
						throw new LeafRedistributeException(
								"delete record failed");

					// adjust the entry pointing to sibling in its parent
					if (deletedKey != null)
						st = parentIndexPage.adjustKey(lastEntry.key,
								deletedKey,tx,table);
					else
						st = parentIndexPage.adjustKey(lastEntry.key,
								firstEntry.key,tx,table);
					if (st == false)
						throw new LeafRedistributeException("adjust key failed");
					return true;
				}
			} else { // 'this' is the right sibling of pptr
				if ((getSlotLength(0,tx,table) + availableSpace(tx,table) + 8) > ((MAX_FRAME - DPFIXED) / 2)) {
					// cannot spare a record for its underflow sibling
					return false;
				} else {
					// move the first record to its sibling

					// get the first record
					KeyDataEntry firstEntry;
					firstEntry = BT.getEntryFromBytes(getData(),
							getSlotOffset(0,tx,table), getSlotLength(0,tx,table), keyType,
							NodeType.LEAF,tx,table);

					// insert it into its sibling
					RID dummyRid = new RID();
					leafPage.insertRecord(firstEntry,tx,table);

					// delete the first record from the old page
					RID delRid = new RID();
					delRid.pageNo = getCurPage(tx,table);
					delRid.slotNo = 0;
					if (deleteSortedRecord(delRid,tx,table) == false) {
						throw new LeafRedistributeException(
								"delete record failed");
					}
					// get the current first record of the old page
					// for adjusting parent pointer.
					KeyDataEntry tmpEntry;
					tmpEntry = getFirst(dummyRid,tx,table);

					// adjust the entry pointing to itself in its parent
					st = parentIndexPage
							.adjustKey(tmpEntry.key, firstEntry.key,tx,table);
					if (st == false) {
						throw new LeafRedistributeException("adjust key failed");
					}
					return true;
				}
			}
		} catch (Exception e) {
			throw new LeafRedistributeException("redistribute failed");
		}
	}

}
